/***********************
Utility Functions
***********************/

/*
Global Variables
/*



/*
Function prototypes.
*/
void WriteDetour(const DWORD dwSource, const DWORD dwDestination, const DWORD dwPadSize, const bool bType);
void WriteMemory(const DWORD dwDestination, DWORD dwSource, const DWORD dwDataLength);
bool SetDebugPrivileges(const HANDLE PROC_HANDLE_IN);
HWND GetCurrentProcessWindow(void);

// WriteDetour function.
/* Parameters: The first parameter is an address in the form of a dword. It is the location where the detour will be written to in memory.
 The second parameter is an address in the form of a dword. It is the location in memory where the detour will lead. The third parameter
 is an optional dword. If NULL is passed as it's value it will be disregarded. Otherwise, it is the amount of bytes to fill with NOP
 or no-operation instructions. The final parameter is a boolean which specifies which type of detour is desired. A value of true will
 result in a JMP detour, whereas a value of false will yield a CALL detour. */
// Return Value: None.
void WriteDetour(const DWORD dwSource, const DWORD dwDestination, const DWORD dwPadSize, const bool bType) {
	// This is just for testing to let you know the WriteDetour function was successfully called and report it's parameters.
	if (bDebugMode == 1) {
		char szBuffer[255] = {0};
		wsprintf(szBuffer, "A detour is to be written from %[#][0][8]X to %[#][0][8]X. \'bType\' is set to %i. dwPadSize is set to %u. The base code for this detour without NOPS will look like: %X%X",dwSource, dwDestination, (int)bType, dwPadSize,((int)bType + 0xE8),((dwDestination-dwSource)-5));
		MessageBox(GetCurrentProcessWindow(),szBuffer,"[WriteDetour Status]",MB_OK | MB_ICONINFORMATION);
	}
	// Create a buffer for the byte code data.
	static BYTE DataBuffer[255] = {0};
	// Create a couple place holders.
	DWORD a = dwSource, b = dwDestination;
	// Swap values and calculate the relative distance operand.
	a = (a + 5);
	b = (b - a);
	// Is 'bType' true?
	if (bType) {
		*(BYTE*)((DWORD)&DataBuffer) = 0xE9; // If so, establish a JMP-based detour.
	} else {
		*(BYTE*)((DWORD)&DataBuffer) = 0xE8; // Otherwise, establish a CALL-based detour.
	}
	// Write the op-codes operand.
	*(DWORD*)((DWORD)&DataBuffer + 1) = b;
	// Has padding been requested?
	for (DWORD i = 1; i <= dwPadSize; i++) {
		*(BYTE*)((DWORD)&DataBuffer+5+i) = 0x90; // If so, pad as needed.
	}
	// Write the byte code to memory.
	WriteMemory(dwSource,(DWORD)&DataBuffer,(5+dwPadSize));

}

// WriteMemory function.
/* Parameters: The first parameter is an address in the form of a dword. It is the location in memory where the desired data will be
 written to. The second parameter is an address to a byte or an array of bytes in the form of a dword, which will serve as the location
 of the data to be written to the location in memory specified by the first parameter. The final parameter is the length or size of the 
 data in bytesto be written. */
// Return Value: None.
void WriteMemory(const DWORD dwDestination, DWORD dwSource, const DWORD dwDataLength) {
	// This is just for testing to let you know the WriteMemory function was successfully called and report it's parameters.
	if (bDebugMode == 1) {
		char szBuffer[255] = {0};
		wsprintf(szBuffer, "Data will be written to the location %[#][0][8]X. The length of the data is %u bytes.",dwDestination,dwDataLength);
		MessageBox(GetCurrentProcessWindow(),szBuffer,"[WriteMemory Status]",MB_OK | MB_ICONINFORMATION);
	}
	// Establish a placeholder for the old access flags of a virtual memory page.
	DWORD dwOldProtect = NULL;
	// Gain read/write/execute access to the relevant virtual memory page.
	VirtualProtect((LPVOID)dwDestination,dwDataLength,PAGE_EXECUTE_READWRITE,&dwOldProtect);
	// Write the data to memory.
	memmove((LPVOID)dwDestination, (LPCVOID)dwSource, dwDataLength);
	// Restore the original access flags for the affected virtual memory page.
	VirtualProtect((LPVOID)dwDestination,dwDataLength,dwOldProtect,&dwOldProtect);

}

// SetDebugPrivileges function. This function gives debugger privileges to a process signified by it's parameter.
/* Parameters: This function has one parameter, a process handle. If the process handle is NULL, the function affects the current
 process. Otherwise, the function affects the process of the handle it receives. */
// Return Value: If the function was successful, the return value is true. Otherwise, the return value is false.
bool SetDebugPrivileges(const HANDLE PROC_HANDLE_IN) {

	LUID Luid;
	TOKEN_PRIVILEGES tpToken;
	HANDLE hToken;
	HANDLE PROC_HANDLE = PROC_HANDLE_IN;

	if (PROC_HANDLE == NULL) {
		PROC_HANDLE = GetCurrentProcess();
	}

	if (!OpenProcessToken(PROC_HANDLE, TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES, &hToken)) {
		return (false);
	}

	if (!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &Luid)) {
		CloseHandle (hToken);
		return (false);
	}

	tpToken.PrivilegeCount = 1;
	tpToken.Privileges[0].Luid = Luid;
	tpToken.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;

	if (!AdjustTokenPrivileges(hToken, false, &tpToken, NULL, NULL, NULL)) {
		CloseHandle(hToken);
		return (false);
	}

	CloseHandle(hToken);
	return (true);

}

// GetCurrentProcessWindow function. Retrieves the window handle to the current processes' parent window.
// Parameters: None.
// Return Value: If the function was successful, the return value is the handle to the window. Otherwise, the return value is NULL.
HWND GetCurrentProcessWindow(void) {
	// Get the window with focus.
	HWND hWnd = GetTopWindow(NULL);
	while (hWnd) {
		DWORD dwProcId;
		// Retrieve the proc id for 'hWnd'.
		DWORD dwTempThreadId = GetWindowThreadProcessId(hWnd,&dwProcId);
		// Does the proc id match that of our host process?
		if (dwProcId == GetCurrentProcessId()) {
			// Return 'hWnd'.
			return (hWnd);
		}
		// Get the next open window.
		hWnd = GetNextWindow(hWnd, GW_HWNDNEXT);
	}
	// If our processes' window was not found, return NULL.
	return (NULL);

}